import {
  createMemo,
  createSignal,
  Index,
  mergeProps,
  splitProps,
  type Component,
} from "solid-js";
import type { SelectOption, FcSelectProps } from "./types";
import { wrapClasses } from "@lib/types";
import { IsNotNullOrUndefined } from "@lib/helper";
import type { JSX } from "solid-js/jsx-runtime";
import type { FcValue } from "../types";
import { GetElementRef } from "../directives";

const defaultProps: Partial<FcSelectProps> = {
  optionToLabel: "label",
  optionToValue: "value",
};

const getRef = GetElementRef;

const toArray = (value: FcValue | FcValue[]) => {
  if (Array.isArray(value)) {
    return value;
  }
  return [value];
};

const FcSelect: Component<FcSelectProps> = (props) => {
  const [local, elProps] = splitProps(mergeProps(defaultProps, props), [
    "value",
    "onValueChanged",
    "ref",
    "$ServerOnly",
    "classList",
    "controlSize",
    "options",
    "optionToLabel",
    "optionToValue",
    "children",
  ]);

  const claseList = createMemo(() => {
    return {
      ...wrapClasses(local.classList),
      [`form-select-${local.controlSize}`]: IsNotNullOrUndefined(
        local.controlSize
      ),
    };
  });

  const optionToValue = createMemo(() =>
    typeof local.optionToValue === "function"
      ? local.optionToValue
      : (option: SelectOption) => option[local.optionToValue as string]
  );

  const optionToLabel = createMemo(() =>
    typeof local.optionToLabel === "function"
      ? local.optionToLabel
      : (option: SelectOption) => option[local.optionToLabel as string]
  );

  const values = createMemo(() => {
    if (local.value) {
      return toArray(local.value);
    }
    return [];
  });

  const options = createMemo(() => {
    const o2v = optionToValue();
    const o2l = optionToLabel();
    const vals = values();

    return local.options
      ?.map((item) => {
        if (typeof item === "object") {
          return {
            label: o2l(item),
            value: o2v(item),
          };
        } else {
          return {
            label: item,
            value: item,
          };
        }
      })
      .map((item) => {
        return {
          ...item,
          selected: vals?.includes(item.value),
        };
      });
  });

  const handleValueChanged: JSX.SelectHTMLAttributes<HTMLSelectElement>["onChange"] =
    (e) => {
      const allOptions = ref()?.options;
      const selectedValues: FcValue[] = [];
      Array.from({
        length: allOptions?.length ?? 0,
      }).forEach((_, index) => {
        if (allOptions?.[index].selected) {
          selectedValues.push(allOptions?.[index].value);
        }
      });
      elProps.multiple
        ? local.onValueChanged?.(selectedValues)
        : local.onValueChanged?.(selectedValues[0]);
    };

  const [ref, setRef] = createSignal<HTMLSelectElement | undefined>(undefined);

  return (
    <select
      ref={local.ref}
      use:getRef={setRef}
      class="form-select"
      {...elProps}
      classList={claseList()}
      onChange={handleValueChanged}
    >
      <Index each={options()}>
        {(opt, index) => (
          <option value={opt().value} selected={opt().selected}>
            {opt().label}
          </option>
        )}
      </Index>
    </select>
  );
};

export type * from "./types";
export default FcSelect;
